// ----------------
// Project:
// ESA Elevator
// ----------------
//
// Description:
// ----------------
// next_floor_ctrl.v testbench
//
// Version History:
// ----------------
// 140116: changed delay between request and check to 2*(NUM_FLOORS+1)*CLK_PERIOD

`timescale 1ns / 1ps

module next_floor_ctrl_tb_public;

  //****************** SIMULATION PARAMETERS *******************//  
  localparam CLK_PERIOD    =  50; // [ns] -> 20 MHz
  localparam NUM_FLOORS    =  99; // 99 floors
  localparam FLOOR_BITS    =   7; // 7 floor bits
  localparam UP            =   2;
  localparam DOWN          =   1;
  //***********************************************************//
  

  //********************* MODULE INPUTS ***********************// 
  reg CLK;
  reg RESET;
  reg [(NUM_FLOORS-1)    :0] SELECTED_FLOORS; 
  reg [((NUM_FLOORS*2)-1):0] REQUESTED_FLOORS;
  reg                        HALTED;  
  reg [(FLOOR_BITS-1):0]     CURRENT_FLOOR;
  //***********************************************************//  


  //********************* MODULE OUTPUTS **********************//
  wire [(FLOOR_BITS-1):0] NEXT_FLOOR;
  //***********************************************************// 


  //******************* UUT INSTANTIATION *********************// 
  next_floor_ctrl #(.FLOORS     (NUM_FLOORS), 
                    .FLOOR_BITS (FLOOR_BITS)) 
            uut (
                 .CLK           (CLK),
                 .RESET         (RESET),
                        
                 .DESTINATIONS  (SELECTED_FLOORS),
                 .FLOOR_REQUEST (REQUESTED_FLOORS),
                 .HALTED        (HALTED),
                 .CURRENT_FLOOR (CURRENT_FLOOR),
                 .NEXT_FLOOR    (NEXT_FLOOR));
  //***********************************************************//
 
  //******************* TESTBENCH SIGNALS *********************//  
  integer expected_next_floor;
  //***********************************************************//                                    

  //******************* 20 MHz CLOCK SIGNAL *******************//   
  always begin 
    #(CLK_PERIOD/2) CLK = ~CLK;
  end
  //***********************************************************// 


  //********************* TEST INITIATION *********************//  
  initial begin
    // Initialize Inputs
    CLK                = 0;
    RESET              = 1;
    CURRENT_FLOOR      = 0;
    HALTED             = 1;
    SELECTED_FLOORS    = 0;
    REQUESTED_FLOORS   = 0;

    // Wait 125 ns for global reset to finish
    #(3*CLK_PERIOD/2);
    RESET = 0;
    $display("************ STARTING SIMULATION ************");  
    #(CLK_PERIOD);
    //--------------------------
    // TARGET FLOOR 1 
    //--------------------------
    select_floor(1);
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 1;
    #(10*CLK_PERIOD);
    //--------------------------
    // TARGET FLOOR 1 OK -> NEW TARGET FLOOR 2 
    //--------------------------
    halt_floor(1);
    select_floor(2);
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 2;
    #(10*CLK_PERIOD);
    //--------------------------
    // ADD TARGET FLOOR 4, REQUEST UP FROM FLOOR 0 
    //--------------------------
    halt_floor(2);
    select_floor(4);
    request_floor(UP, 0);
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 4;
    #(10*CLK_PERIOD);
    //--------------------------
    // CLEAR TARGET FLOOR 2, REQUEST UP FROM FLOOR 3 
    //--------------------------
    halt_floor(2);
    select_floor(0);
    request_floor(UP, 3);
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 3;
    #(10*CLK_PERIOD);
    //--------------------------
    // CLEAR TARGET FLOOR 3
    //--------------------------
    halt_floor(3);
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 4;
    #(10*CLK_PERIOD);
    //--------------------------
    // CLEAR TARGET FLOOR 4, ADD TARGET FLOOR 1
    //--------------------------
    halt_floor(4);
    select_floor(1);
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 1;
    #(10*CLK_PERIOD);
    //--------------------------
    // REQUEST DOWN FROM FLOOR 2, UP FROM FLOOR 3
    //--------------------------
    CURRENT_FLOOR = 3;
    request_floor(DOWN, 2);
    request_floor(UP, 3);
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 2;
    #(10*CLK_PERIOD);
    //--------------------------
    // CLEAR TARGET FLOOR 2
    //--------------------------
    halt_floor(2);
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 1;
    #(10*CLK_PERIOD);
    //--------------------------
    // CLEAR TARGET FLOOR 1
    //--------------------------
    halt_floor(1);
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 0;
    #(10*CLK_PERIOD);
    //--------------------------
    // CLEAR TARGET FLOOR 3
    //--------------------------
    halt_floor(0);
    request_floor(DOWN, 2);
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 3;
    #(10*CLK_PERIOD);
    $display("************ SIMULATION COMPLETE ************");
    $finish;
    //-------------------------- 
  end
  //***********************************************************// 
  
initial begin
  #(100000*CLK_PERIOD)
  $display("************ SIMULATION KILLED BECAUSE OF ERROR ************");
  $finish;
end
  
  //********************* SIMULATION TASKS ********************//
  task request_floor;
     input [((NUM_FLOORS*2)-1):0]                 direction;
     input [FLOOR_BITS-1:0] floor;
     begin
         REQUESTED_FLOORS = REQUESTED_FLOORS | ((direction) << (2*floor));
     end
  endtask
  
  task select_floor;
     input [FLOOR_BITS-1:0] floor;
     begin 
       SELECTED_FLOORS = SELECTED_FLOORS | (1 << (floor));
     end
  endtask
  

  task halt_floor;
     input [FLOOR_BITS-1:0] floor;
     begin
       CURRENT_FLOOR    = floor;
       HALTED           = 1;
       SELECTED_FLOORS  = SELECTED_FLOORS  & (~(1 << (floor)));
       REQUESTED_FLOORS = REQUESTED_FLOORS & (~(3 << (2*floor)));
    end
  endtask 
  //***********************************************************// 
  
   
  //****************** VISUALISATION PROCESS ******************// 
  always @(NEXT_FLOOR) begin
    if(CURRENT_FLOOR != NEXT_FLOOR) begin
      $display("driving to floor %d", NEXT_FLOOR);
      #(CLK_PERIOD);
      HALTED = 0;
    end
  end   
            
  always @(expected_next_floor) begin
    if(expected_next_floor == NEXT_FLOOR) begin
      $display("next floor computed correctly - driving to floor %d", NEXT_FLOOR);
    end
    else begin
      $display("error: next floor is %d instead of %d", NEXT_FLOOR, expected_next_floor);     
    end
  end
  //***********************************************************// 
      
endmodule
